/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package util;

import java.sql.SQLException;
import java.util.ArrayList;
import util.Conexao;

/**
 *
 * @author bruno
 */
public abstract class Dao< M extends Modelo> {

    protected Conexao conexao;
    protected M modelo;

    /**
     *
     * Inicializar a conexão com a driver,username, password e a base de dados
     */
    public Dao(Conexao conexao) {
        this.conexao = conexao;
    }

    /**
     * 
     * Recebe a intrução para gravar e o modelo a ser gravado modifica o modelo
     * no campo id com o numero gerado pelo auto increment facilitando assim as
     * inserções dependentes. Devolve true se a inserção for feita com sucesso
     */
    protected boolean gravarModelo(String intrucao, M modelo) {
        boolean estado = false;
        this.modelo = modelo;
        try {
            conexao.iniciar();
            conexao.prepararInstrucao(intrucao + "SELECT SCOPE_IDENTITY() AS id");

            definirParametrosGravar();

            if (conexao.executarInstrucao() && conexao.getStatement().getMoreResults()) {
                conexao.getStatement().getResultSet().next();
                modelo.setId(conexao.getStatement().getResultSet().getInt("id"));
                estado = true;
            }
        } catch (SQLException ex) {
            System.err.println("Erro ao gravar no " + this.getClass().getName() + ": " + ex.getMessage());
        } finally {
            conexao.fecharTudo();
            return estado;
        }
    }

    /**
     * 
     * Recebe a intrução para gravar e o id do modelo a ser lido retorna o
     * modelo se ler ser erros e null em caso de erro
     */
    protected M lerModelo(String intrucao, int id) {
        try {
            conexao.iniciar();
            conexao.prepararInstrucao(intrucao);
            conexao.getStatement().setInt(1, id);
            modelo = null;

            if (conexao.executarInstrucao()) {
                conexao.getResultSet().next();
                modelo = novoModelo();
                modelo.setId(id);
                definirParametrosLer();
            }
        } catch (SQLException ex) {
            System.err.println("Erro ao ler no " + this.getClass().getName() + ": " + ex.getMessage());
        } finally {
            conexao.fecharTudo();
            return modelo;
        }
    }

    /**
     * 
     * Recebe a intrução para retorna uma lista de modelo Podendo ser
     * personalisado essa intrução atravez de parametros Nota: ao personalizar
     * atenção no usso das '(plicas) no SQL
     */
    protected ArrayList<M> lerTodos(String instrucao) {
        ArrayList modeloLista = new ArrayList();
        try {
            conexao.iniciar();
            if (conexao.executarInstrucao(instrucao)) {
                while (conexao.getResultSet().next()) {
                    modelo = novoModelo();
                    modelo.setId(conexao.getResultSet().getInt(1));
                    definirParametrosLer();
                    modeloLista.add(modelo);
                }
            }
        } catch (SQLException ex) {
            System.err.println("Erro ao ler todos no " + this.getClass().getName() + ": " + ex.getMessage());
        } finally {
            conexao.fecharTudo();
            return modeloLista;
        }
    }

    /**
     * 
     * Subscrever com a instancia do modelo Ex. de utilização: return new
     * Xmodelo();
     */
    protected abstract M novoModelo();

    /**
     * 
     * Este metodo abstracto deve ser subscrito reutilizando o metodo
     * gravarModelo() por dentro. A inserção é totalmente dependente do metodo
     * definirParametrosGravar() Ex. de utilização: return gravarModelo("INSERT
     * INTO xmodelo(nome,numero) VALUES ( ?, ?)",xmodelo);
     */
    public abstract boolean gravar(M modelo);

    /**
     * 
     * Indicar como os parametros têm de ser gravados do modelo para o statment
     */
    protected abstract void definirParametrosGravar() throws SQLException;

    /**
     * 
     * Este metodo abstracto deve ser subscrito reutilizando o metodo
     * lerModelo() por dentro. A leitura é totalmente dependente do metodo
     * definirParametrosLer() Ex. de utilização: return lerModelo("SELECT * FROM
     * xModelo WHERE id = ? ", id);
     */
    public abstract M ler(int id);

    /**
     * 
     * Indicar como os parametros têm de ser lidos do resultset para o modelo
     */
    protected abstract void definirParametrosLer() throws SQLException;

    /**
     * 
     * Este metodo abstracto deve ser subscrito reutilizando o metodo
     * lerTodos() por dentro. A leitura é totalmente dependente do metodo
     * definirParametrosLer() Ex. de utilização: 
     * return lerTodos("SELECT * FROM xModelo");
     */
    public abstract ArrayList<M> lerTodos();
}